#include "filetransfferwork.h"
#include <filetransfferwork.h>

#include <session.h>

void FileTransfferWork::start() {
	isStop_ = false;
	workThread_ = std::make_shared<std::thread>([&]() {
		while (!isStop_) {
			std::unique_lock<std::mutex> locker(logicLock_);
			while (!isStop_ && logicQueue_.empty()) {
				logicCond_.wait(locker);
			}
			if (isStop_)return;
			auto logicNode = logicQueue_.front();
			logicQueue_.pop();
			locker.unlock();
			handler(logicNode);
		}
	});
}

void FileTransfferWork::postTask(std::shared_ptr<LogicNode> logicNode)
{
	std::lock_guard<std::mutex> locker(logicLock_);
	if (logicQueue_.size() >= MAX_QUEUE_SIZE) {
		std::cerr << "thread:" << std::this_thread::get_id() << " queue size out of range, size: " << logicQueue_.size() << std::endl;
		return;
	}
	logicQueue_.push(logicNode);
	logicCond_.notify_one();
}

void FileTransfferWork::stop() {
	isStop_ = true;
	logicCond_.notify_all();
	workThread_->join();
	workThread_.reset();
	std::lock_guard<std::mutex> locker(logicLock_);
	while (!logicQueue_.empty()) {
		auto logicNode = logicQueue_.front();
		logicQueue_.pop();
		handler(logicNode);
	}
}

bool FileTransfferWork::sendFile(FileType type, const std::string& filepath,std::shared_ptr<Session> session) {
	//nodes.clear();
	std::fstream file;
	if(!std::filesystem::exists(filepath)) { // 判断文件是否存在
		std::cerr << filepath << " no exists!" << std::endl;
		auto errorNode = getErrorNode(MsgType::TYPE_FILE, FileErrorType::FILE_NO_EXISTS, "file no exists!");
		session->postSendNode(errorNode);
		//nodes.push_back(errorNode);
		return false;
	}
	file.open(filepath, std::ios::in | std::ios::binary); // note: 这里需要用二进制打开，否则由于unix、Windows下的换行符不同会导致发送的文件大小和真实文件大小不同
	if (!file.is_open()) { // 判断文件是否打开
		std::cerr << filepath << "open failed!" << std::endl;
		auto errorNode = getErrorNode(MsgType::TYPE_FILE, FileErrorType::FILE_OPEN_FAIL, "file open failed!");
		//nodes.push_back(errorNode); 
		session->postSendNode(errorNode);
		return false;
	}
	// 获取文件总大小
	file.seekg(0, std::ios::end);
	size_t fileSize = file.tellg();
	std::cout << "start send file " << filepath << " size: " << fileSize << std::endl;
	size_t bytesSend = 0;
	file.seekg(std::ios::beg);
	char buffer[FILE_CHUNK_SIZE] = { 0 };
	Json::Value sendObj;
	while (bytesSend < fileSize) {
		memset(buffer, 0, FILE_CHUNK_SIZE);
		file.read(buffer, FILE_CHUNK_SIZE);
		size_t readBytes = file.gcount();
		sendObj["data"] = std::string(buffer, readBytes);
		sendObj["dataLen"] = readBytes;
		sendObj["filename"] = "test_" + session->getId() + ".pdf"; // 暂时写死,测试用
		if (bytesSend + readBytes >= fileSize) {
			sendObj["type"] = static_cast<int>(getFileFinishType(type));
		}
		else {
			sendObj["type"] = static_cast<int>(getFileResponseType(type));
		}
		//if (bytesSend + readBytes > 8593408) {
		//	std::cout << "debug" << std::endl;
		//}
		Json::StreamWriterBuilder builder;
		std::string respBody = Json::writeString(builder, sendObj);
		std::shared_ptr<SendNode> sendNode = std::make_shared<SendNode>(respBody.data(), static_cast<int>(MsgType::TYPE_FILE), respBody.size());
		//nodes.push_back(sendNode);
		session->postSendNode(sendNode);
		//std::this_thread::sleep_for(std::chrono::milliseconds(10));
		bytesSend += readBytes;
		std::cout << "file<" << filepath << "> has parse bytes: " << bytesSend << " readbytes: " << respBody.size() << std::endl;
	}
	file.close();
	return true;
}

std::shared_ptr<SendNode> FileTransfferWork::readFile(FileType type, const std::shared_ptr<FileTransfferStruct>& transferStruct)
{
	Json::Value value;
	char buffer[FILE_CHUNK_SIZE];
	value["ack"] = transferStruct->getSeq();
	size_t bytesRead = transferStruct->processBytes(buffer, FILE_CHUNK_SIZE); // ++seq_
	value["seq"] = transferStruct->getSeq();
	value["data"] = std::string(buffer, bytesRead);
	value["dataLen"] = bytesRead;
	value["filename"] = "test_" + transferStruct->getFileName();
	if (transferStruct->isProcessFinish()) {
		value["type"] = static_cast<int>(getFileFinishType(type));
		FileTransfferMgr::getInstance().removeFileStruct(transferStruct->getSessionId(), transferStruct->getFileName());
	}
	else {
		value["type"] = static_cast<int>(getFileResponseType(type));
	}
	Json::StreamWriterBuilder builder;
	std::string json2Str = Json::writeString(builder, value);
	std::shared_ptr<SendNode> sendNode = std::make_shared<SendNode>(json2Str.c_str(), static_cast<int>(MsgType::TYPE_FILE), json2Str.size());
	return sendNode;
}


void FileTransfferWork::handler(std::shared_ptr<LogicNode> logicNode)
{
	auto recvNode = logicNode->recvNode_;
	auto session = logicNode->session_;
	int msgId = recvNode->msgId_;
	Json::Value fileObj;
	Json::CharReaderBuilder builder;
	std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
	std::string err;
	if(!reader->parse(recvNode->data_, recvNode->data_ + recvNode->totalLen_, &fileObj, &err)) {
		std::cerr << "json parse error: " << err << std::endl;
		return;
	}
	int type = fileObj["type"].asInt();
	if (type == static_cast<int>(FileType::TYPE_GET_FILE_TREE)) {
		// 写入文件
		Json::Value root;
		std::string filePath = std::string(FILE_PATH) + "/fileTree.txt";
		std::fstream fileTree(filePath, std::ios::out | std::ios::binary);
		if (Util::MiscUtil::getFileTree(FILE_PATH, root)) {
			Json::StreamWriterBuilder builder;
			const std::string fileTreeStr = Json::writeString(builder, root);
			fileTree << fileTreeStr;
		}
		else {
			fileTree << "failed!";
		}
		fileTree.close();
		// 发送文件
		//std::vector <std::shared_ptr<SendNode>> nodes;
		if (!sendFile(static_cast<FileType>(type), filePath,session)) {
			std::cerr << "parse file error!" << std::endl;
		}
		//for (auto node : nodes) {
		//	session->postSendNode(node);
		//}
	}
	else if (type == static_cast<int>(FileType::TYPE_FILE_DOWNLOAD)) {
		std::string filePath = std::string(FILE_PATH) + "/" + fileObj["filename"].asString();
		int seq = fileObj["seq"].asInt();
		int64_t hasRecv = fileObj["hasRecv"].asInt64();
		std::cout << "filename: " << filePath << " seq: " << seq << " hasRecv: " << hasRecv << std::endl;
		auto& transfferMgr = FileTransfferMgr::getInstance();
		// seq == 0，第一个请求包，要打开文件
		if (seq == 0) {
			if (transfferMgr.existFileStruct(session->getId(), filePath)) {
				throw std::runtime_error(std::string(session->getId() + " 不应该打开文件：" + filePath));
				auto errorNode = getErrorNode(MsgType::TYPE_FILE, FileErrorType::FILE_OPEN_FAIL, "500 sever open file error!");
				session->postSendNode(errorNode);
				return;
			}
			// 创建filetransfferstruct
			std::shared_ptr<FileTransfferStruct> fileReadStruct = std::make_shared<FileReadStruct>(session->getId());
			if (!fileReadStruct->openFile(filePath)) {
				std::cerr << filePath << "open failed!" << std::endl;
				auto errorNode = getErrorNode(MsgType::TYPE_FILE, FileErrorType::FILE_OPEN_FAIL, "file open failed!");
				session->postSendNode(errorNode);
				return;
			}
			transfferMgr.appendFileStruct(fileReadStruct);
		}
		std::shared_ptr<FileTransfferStruct> transferStruct = FileTransfferMgr::getInstance().getFileStruct(session->getId(), filePath);
		if (!transferStruct) { // 找不到文件传输结构体，报错
			session->close();
			std::runtime_error(session->getId() + ": FileTransferMgr can't find file: " + filePath);
			return;
		}

		// todo:发送文件，从transfferMgr中每次读取一个FILE_CHUNK_SIZE的响应包发送给文件，
		//		等待客户端接收后，发送下一个响应包，直到文件传输结束transferMgr要removeFileStruct
		auto sendNode = readFile(static_cast<FileType>(type), transferStruct);
		session->postSendNode(sendNode);
	}
	else if (type == static_cast<int>(FileType::TYPE_FILE_UPLOAD)) {

	}
	else if (type == static_cast<int>(FileType::TYPE_FILE_RETRY_DOWNLOAD)) {

	}
	else if (type == static_cast<int>(FileType::TYPE_FILE_RETRY_UPLOAD)) {

	}
	else {
		throw std::runtime_error("unknown msg type: " + std::to_string(msgId));
	}
}

FileTransfferWork::~FileTransfferWork()
{
}

FileType FileTransfferWork::getFileResponseType(FileType type) {
	switch (type)
	{
	case FileType::TYPE_GET_FILE_TREE:
		return FileType::TYPE_GET_FILE_TREE_RESPONSE;
		break;
	case FileType::TYPE_FILE_UPLOAD:
		return FileType::TYPE_FILE_UPLOAD_RESPONSE;
		break;
	case FileType::TYPE_FILE_DOWNLOAD:
		return FileType::TYPE_FILE_DOWNLOAD_RESPONSE;
		break;
	default:
		return FileType::TYPE_ERROR;
		break;
	}
}

FileType FileTransfferWork::getFileFinishType(FileType type) {
	switch (type)
	{
	case FileType::TYPE_GET_FILE_TREE:
		return FileType::TYPE_GET_FILE_TREE_FINISH;
		break;
	case FileType::TYPE_FILE_UPLOAD:
		return FileType::TYPE_FILE_UPLOAD_RESPONSE_FINISH;
		break;
	case FileType::TYPE_FILE_DOWNLOAD:
		return FileType::TYPE_FILE_DOWNLOAD_RESPONSE_FINISH;
		break;
	default:
		return FileType::TYPE_ERROR;
		break;
	}
}

std::shared_ptr<SendNode> FileTransfferWork::getErrorNode(MsgType msgType, FileErrorType errorType, std::string msg) {
	std::shared_ptr<SendNode> errorNode = std::make_shared<SendNode>(msg.data(), static_cast<int>(msgType), msg.size());
	return errorNode;
}